package ws

import (
	"github.com/gorilla/websocket"
	"go.uber.org/zap"
	"gogogo-server/engine/lgconf"
	"sync"
	"time"
)

// TODO 心跳

// LGClient WS的客户端
type LGClient struct {
	cfg *lgconf.LGClientConfig

	dialer     websocket.Dialer // websocket的连接配置
	conn       *websocket.Conn
	fnNewAgent TypeFnNewAgent

	mu              sync.Mutex
	closeFlag       bool
	closeServer     bool      // 关服时这个会为true
	closeChan       chan bool // 关闭的时候发消息,停止重连
	wg              sync.WaitGroup
	nextConnectable time.Time // 下次可重连时间
}

func NewClient(cfg *lgconf.LGClientConfig, fn TypeFnNewAgent) *LGClient {
	return &LGClient{
		cfg:        cfg,
		fnNewAgent: fn,
		closeChan:  make(chan bool),
	}
}

func (c *LGClient) fixConfig() {
	if c.cfg.ReconnectInterval < 3*time.Second {
		c.cfg.ReconnectInterval = 3 * time.Second
		zap.S().Warnf("[%s]重连时间修正为:%d", c.cfg.Name, c.cfg.ReconnectInterval)
	}
	if c.cfg.HeartBeatInterval < 10*time.Second {
		c.cfg.HeartBeatInterval = 30 * time.Second
		zap.S().Warnf("[%s]心跳时间修正为:%d", c.cfg.Name, c.cfg.HeartBeatInterval)
	}
	if c.cfg.Timeout < 1 {
		c.cfg.Timeout = 10 * time.Second
		zap.S().Warnf("[%s]超时时间修正为:%d", c.cfg.Name, c.cfg.Timeout)
	}
	if c.cfg.MaxMsgLen < 1 {
		c.cfg.MaxMsgLen = 1000000
		zap.S().Warnf("[%s]超时消息长度修正为:%d", c.cfg.Name, c.cfg.MaxMsgLen)
	}
	if c.cfg.SizeWriteChan < 1 {
		c.cfg.SizeWriteChan = 100000
		zap.S().Warnf("[%s]消息队列长度修正为:%d", c.cfg.Name, c.cfg.SizeWriteChan)
	}
	c.dialer = websocket.Dialer{
		HandshakeTimeout: c.cfg.Timeout,
	}
}

func (c *LGClient) Start() {
	c.fixConfig()
	c.wg.Add(1)
	go c.connect()
}

func (c *LGClient) doDial() *websocket.Conn {
	for {
		waitTime := c.nextConnectable.Sub(time.Now())
		if waitTime <= 0 {
			c.nextConnectable = time.Now().Add(c.cfg.ReconnectInterval)
			conn, _, err := c.dialer.Dial(c.cfg.Addr, nil)
			if err == nil || c.closeFlag {
				return conn
			}
			zap.S().Warnf("[%s] 连接失败, 等待[%f]秒后重连", c.cfg.Name, c.cfg.ReconnectInterval.Seconds())
			waitTime = c.cfg.ReconnectInterval
		}
		select {
		case <-time.After(waitTime):
		case <-c.closeChan:
			return nil
		}
	}
}

func (c *LGClient) connect() {
	defer c.wg.Done()
	defer func() {
		select {
		case <-c.closeChan:
		default:
		}
	}()
	for {
		conn := c.doDial()
		if nil == conn {
			break
		}
		c.mu.Lock()
		if c.closeFlag {
			c.mu.Unlock()
			c.Close()
			break
		}
		c.conn = conn
		c.mu.Unlock()
		conn.SetReadLimit(int64(c.cfg.MaxMsgLen))
		wsConn := NewConn(conn, c.cfg.MaxMsgLen, c.cfg.SizeWriteChan)
		a := c.fnNewAgent(wsConn)
		a.AgentRun()

		wsConn.Close()
		a.AgentClose()
		c.conn = nil
		c.mu.Lock()
		if c.closeServer {
			c.mu.Unlock()
			break
		}
		c.mu.Unlock()
	}
}

func (c *LGClient) Close() {
	c.mu.Lock()
	c.closeServer = true
	c.closeFlag = true
	if c.conn != nil {
		c.conn.Close()
	}
	c.conn = nil
	c.mu.Unlock()
	c.closeChan <- true
	close(c.closeChan)
	c.wg.Wait()
}
